﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Mirror.CliMetadata.Signatures;
using System.Runtime.InteropServices;

namespace Kokomo.Mirror.CliMetadata
{
	abstract class CliMethodInfoBase : MethodInfo
	{
		protected CliMemberBasicInfo basicInfo;

		protected CliMethodInfoBase(
			CliMemberBasicInfo basicInfo,
			string name
			)
		{
			if ((object)basicInfo.module == null)
				throw new ArgumentNullException("basicInfo.module");

			this.basicInfo = basicInfo;
			this._name = name;
			//this.Signature = signature;

			//this._customAttributeData = new Lazy<IList<CustomAttributeData>>(this.LoadCustomAttributeData);
			//this._parameters = new Lazy<ParameterInfo[]>(this.LoadParameters);
		}

		private string _name;
		public sealed override string Name
		{
			get { return this._name; }
		}
		public override string ToString()
		{
			return this.Name;
		}

		public override int MetadataToken
		{
			get { return this.basicInfo.token; }
		}

		public override Module Module
		{
			get { return this.basicInfo.module; }
		}


		#region Attributes

		//private Lazy<IList<CustomAttributeData>> _customAttributeData;
		private IList<CustomAttributeData> LoadCustomAttributeData()
		{
			return this.basicInfo.module.GetCustomAttributesFor(this.MetadataToken);
		}
		public override IList<CustomAttributeData> GetCustomAttributesData()
		{
			return this.LoadCustomAttributeData();
			//return this._customAttributeData.Value;
		}
		#endregion

		#region Parameters

		private ParameterInfo _returnParameter;

		//private Lazy<ParameterInfo[]> _parameters;
		protected abstract ParameterInfo[] LoadParameters(out ParameterInfo returnParameter);

		public override ParameterInfo ReturnParameter
		{
			get
			{
				if (this._returnParameter == null)
					this.GetParameters();

				return this._returnParameter;
			}
		}

		public override Type ReturnType
		{
			get
			{
				ParameterInfo param = this.ReturnParameter;
				if (param != null) return param.ParameterType;
				else return typeof(void);
			}
		}

		public override ParameterInfo[] GetParameters()
		{
			return this.LoadParameters(out this._returnParameter);
			//return this._parameters.Value;
		}
		#endregion

		public override MethodInfo GetBaseDefinition()
		{
			if (
				((object)this.DeclaringType == null)
				|| ((object)this.DeclaringType.BaseType == null)
				|| (!this.IsVirtual)
				|| (this.Attributes.HasFlag(MethodAttributes.NewSlot))
				)
				return this;

			ParameterInfo[] parameters = this.GetParameters();
			Type[] parameterTypes = new Type[parameters.Length];
			//ParameterModifier modifier = new ParameterModifier(parameters.Length);
			for (int i = 0; i < parameters.Length; i++)
			{
				ParameterInfo parameter = parameters[i];
				Type parameterType = parameter.ParameterType;
				parameterTypes[i] = parameterType;
			}

			MethodInfo baseMethod = this.DeclaringType.BaseType.GetMethod(
				this.Name,
				BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
				null,
				parameterTypes,
				null
				);
			if ((object)baseMethod != null)
				baseMethod = baseMethod.GetBaseDefinition();
			else
				baseMethod = this;

			return baseMethod;
		}

		public override ICustomAttributeProvider ReturnTypeCustomAttributes
		{
			get { return this.ReturnType; }
		}

		public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override RuntimeMethodHandle MethodHandle
		{
			get { throw new NotImplementedException(); }
		}

		public override Type DeclaringType
		{
			get { return this.basicInfo.declaringType; }
		}

		public override object[] GetCustomAttributes(Type attributeType, bool inherit)
		{
			throw new NotImplementedException();
		}

		public override object[] GetCustomAttributes(bool inherit)
		{
			throw new NotImplementedException();
		}

		public override bool IsDefined(Type attributeType, bool inherit)
		{
			return
				(this.Attributes.HasFlag(MethodAttributes.PinvokeImpl) && (MemberEqualityComparer.Equal(attributeType, typeof(DllImportAttribute))))
				|| this.basicInfo.module.IsAttributeDefinedOn(this.MetadataToken, attributeType, inherit);
		}

		public override Type ReflectedType
		{
			get { return this.basicInfo.reflectedType; }
		}
	}
}
